Unit mnCommClasses;
{**
 *  This file is part of the "Mini Comm"
 *
 * @license   modifiedLGPL (modified of http://www.gnu.org/licenses/lgpl.html)
 *            See the file COPYING.MLGPL, included in this distribution,
 * @author    Zaher Dirkey <zaher at parmaja dot com>
 *}

  {$M+}
  {$H+}
  {$IFDEF FPC}
  {$MODE delphi}
  {$ENDIF}

Interface

Uses
    Classes, SysUtils,
    mnStreams;

Const
    cTimeout = 30000;

Type
    EComPort = Class(Exception)
    End;

    TDataBits    = (dbFive, dbSix, dbSeven, dbEight);
    TParityBits  = (prNone, prOdd, prEven, prMark, prSpace);
    TStopBits    = (sbOneStopBit, sbOneAndHalfStopBits, sbTwoStopBits);
    TFlowControl = (fcHardware, fcSoftware, fcNone, fcCustom);

    TDTRFlowControl = (dtrDisable, dtrEnable, dtrHandshake);
    TRTSFlowControl = (rtsDisable, rtsEnable, rtsHandshake, rtsToggle);

    TFlowControlFlags = Record
        OutCTSFlow: Boolean;
        OutDSRFlow: Boolean;
        ControlDTR: TDTRFlowControl;
        ControlRTS: TRTSFlowControl;
        XonXoffOut: Boolean;
        XonXoffIn: Boolean;
        DSRSensitivity: Boolean;
        TxContinueOnXoff: Boolean;
        XonChar: AnsiChar;
        XoffChar: AnsiChar;
    End;

    TParityFlags = Record
        Check: Boolean;
        Replace: Boolean;
        ReplaceChar: AnsiChar;
    End;

    TComEvent  = (evRxChar, evTxEmpty, evRxFlag, evRing, evBreak, evCTS, evDSR,
        evError, evRLSD, evRx80Full);
    TComEvents = Set Of TComEvent;

    TmnCommConnectMode = (ccmReadWrite, ccmRead, ccmWrite);

    { TmnCustomCommStream }

    TmnCustomCommStream = Class(TmnCustomStream)
    private
        FBaudRate: Int64;
        FConnectMode: TmnCommConnectMode;
        FParity: TParityBits;
        FStopBits: TStopBits;
        FPort: String;
        FFlowControl: TFlowControl;
        FDataBits: TDataBits;
        FEventChar: AnsiChar;
        FDiscardNull: Boolean;
        FBufferSize: Integer;
        FUseOverlapped: Boolean;
        FTimeout: Cardinal;
        FReadTimeout: Cardinal;
        FWriteThrough: Boolean;
        FWriteTimeout: Cardinal;
        FFailTimeout: Boolean;
        FReadTimeoutConst: Cardinal;
        FWriteTimeoutConst: Cardinal;
        FWaitMode: Boolean;
        FQueMode: Boolean;
        Procedure SetEventChar(Const Value: AnsiChar);
        Procedure SetDiscardNull(Const Value: Boolean);
        Procedure SetBufferSize(Const Value: Integer);
        Procedure SetTimeout(Const Value: Cardinal);
        Procedure SetUseOverlapped(Const Value: Boolean);
        Procedure SetReadTimeout(Const Value: Cardinal);
        Procedure SetWriteTimeout(Const Value: Cardinal);
    protected
        Procedure DoConnect; virtual; abstract;
        Procedure DoDisconnect; virtual; abstract;
        Function GetConnected: Boolean; virtual; abstract;
        Function GetFlowControlFlags: TFlowControlFlags; virtual;
        Function GetParityFlags: TParityFlags; virtual;
        Function DoRead(Var Buffer; Count: Integer): Integer; virtual; abstract;
        Function DoWrite(Const Buffer; Count: Integer): Integer; virtual; abstract;
    public
        Constructor Create(Suspend: Boolean; Port: String; BaudRate: Int64;
            DataBits: TDataBits = dbEight; Parity: TParityBits = prNone;
            StopBits: TStopBits = sbOneStopBit; FlowControl: TFlowControl = fcHardware;
            UseOverlapped: Boolean = False); overload;
        Constructor Create(Params: String); overload;
        Destructor Destroy; override;
        Procedure Connect;
        Procedure Disconnect;
        Procedure Open;  //Alias for Connect
        Procedure Close; //Alias for Disconnect
        Procedure Flush; virtual;
        Procedure Purge; virtual;
        Procedure Reset; virtual;
        Procedure Cancel; virtual;
        Function GetInQue: Integer; virtual;
        Function ReadString: String;
        Function WaitEvent(Const Events: TComEvents): TComEvents; virtual;
        Function Read(Var Buffer; Count: Integer): Integer; override;
        Function Write(Const Buffer; Count: Integer): Integer; override;
        Property Connected: Boolean read GetConnected;
        Property Port: String read FPort;
        Property BaudRate: Int64 read FBaudRate;
        Property DataBits: TDataBits read FDataBits;
        Property Parity: TParityBits read FParity;
        Property StopBits: TStopBits read FStopBits;
        Property FlowControl: TFlowControl read FFlowControl write FFlowControl;
        Property EventChar: AnsiChar read FEventChar write SetEventChar default #0;
        Property DiscardNull: Boolean read FDiscardNull write SetDiscardNull
            default False;
        Property WriteThrough: Boolean read FWriteThrough write FWriteThrough;
        Property BufferSize: Integer read FBufferSize write SetBufferSize;

        Property UseOverlapped: Boolean read FUseOverlapped write SetUseOverlapped;
        Property ConnectMode: TmnCommConnectMode read FConnectMode write FConnectMode;
        //WaitMode: Make WaitEvent before read buffer
        Property WaitMode: Boolean read FWaitMode write FWaitMode;
        Property QueMode: Boolean read FQueMode write FQueMode;
        Property Timeout: Cardinal read FTimeout write SetTimeout default cTimeout;
        //FailTimeout: raise an exception when timeout accord
        Property FailTimeout: Boolean read FFailTimeout write FFailTimeout default True;
        //ReadTimeout: for internal timeout while reading COM will return in this with buffered chars
        //When ReadTimeout = 0 it not return until the whale buffer requested read
        Property ReadTimeout: Cardinal read FReadTimeout write SetReadTimeout default 0;
        Property ReadTimeoutConst: Cardinal 
            read FReadTimeoutConst write FReadTimeoutConst default 10;
        //When WriteTimeout = 0 it not return until the whale buffer requested writen
        Property WriteTimeout: Cardinal read FWriteTimeout write SetWriteTimeout
            default 1000;
        Property WriteTimeoutConst: Cardinal 
            read FWriteTimeoutConst write FWriteTimeoutConst default 10;
    End;

Const
    AllComEvents: TComEvents = [Low(TComEvent)..High(TComEvent)];

Implementation

{ TmnCustomCommStream }

Procedure TmnCustomCommStream.Cancel;
Begin
End;

Procedure TmnCustomCommStream.Close;
Begin
    Disconnect;
End;

Constructor TmnCustomCommStream.Create(Suspend: Boolean;
    Port: String; BaudRate: Int64; DataBits: TDataBits; Parity: TParityBits;
    StopBits: TStopBits; FlowControl: TFlowControl; UseOverlapped: Boolean);
Begin
    Inherited Create;
    FReadTimeout := 0;
    FReadTimeoutConst := 10;
    FWriteTimeout := 100;
    FWriteTimeoutConst := 10;
    FTimeout := cTimeout;
    FFailTimeout := True;
    FPort := Port;
    FBaudRate := BaudRate;
    FDataBits := DataBits;
    FParity := Parity;
    FStopBits := StopBits;
    FFlowControl := FlowControl;
    FUseOverlapped := UseOverlapped;
    If not Suspend Then Open;
End;

Procedure TmnCustomCommStream.Connect;
Begin
    DoConnect;
End;

Constructor TmnCustomCommStream.Create(Params: String);
Begin
End;

Destructor TmnCustomCommStream.Destroy;
Begin
    If Connected Then Disconnect;
    Inherited;
End;

Procedure TmnCustomCommStream.Disconnect;
Begin
    DoDisconnect;
End;

Procedure TmnCustomCommStream.Flush;
Begin
End;

Function TmnCustomCommStream.GetFlowControlFlags: TFlowControlFlags;
Begin
    Result.XonChar := #17;
    Result.XoffChar := #19;
    Result.DSRSensitivity := False;
    Result.ControlRTS := rtsDisable;
    Result.ControlDTR := dtrDisable;//or enable like as Synaser
    Result.TxContinueOnXoff := False;
    Result.OutCTSFlow := False;
    Result.OutDSRFlow := False;
    Result.XonXoffIn := False;
    Result.XonXoffOut := False;
    Case FlowControl Of
        fcHardware:
            Begin
                Result.ControlRTS := rtsHandshake;
                Result.OutCTSFlow := True;
            End;
        fcSoftware:
            Begin
                Result.XonXoffIn := True;
                Result.XonXoffOut := True;
            End;
    End;
End;

Function TmnCustomCommStream.GetInQue: Integer;
Begin
    Result := 0;
End;

Function TmnCustomCommStream.GetParityFlags: TParityFlags;
Begin
    Result.Check := False;
    Result.Replace := False;
    Result.ReplaceChar := #0;
End;

Procedure TmnCustomCommStream.Open;
Begin
    If Connected Then Raise EComPort.Create('Already connected');
    Connect;
End;

Procedure TmnCustomCommStream.Purge;
Begin
End;

Function TmnCustomCommStream.Read(Var Buffer; Count: Integer): Integer;
Begin
    If WaitMode Then
    Begin
        If WaitEvent([evRxChar, evRxFlag]) <> [] Then
        Begin
            If QueMode Then Count := GetInQue;
            If Connected Then Result := DoRead(Buffer, Count)
        End
        Else Result := 0;
    End
    Else
    Begin
        If QueMode Then Count := GetInQue;
        Result := DoRead(Buffer, Count);
    End;
End;

Function TmnCustomCommStream.ReadString: String;
Var
    c: Integer;
Begin
    c := 255;
    SetLength(Result, c);
    c := Read(Result[1], c);
    SetLength(Result, c);
End;

Procedure TmnCustomCommStream.Reset;
Begin
End;

Procedure TmnCustomCommStream.SetBufferSize(Const Value: Integer);
Begin
    FBufferSize := Value;
End;

Procedure TmnCustomCommStream.SetReadTimeout(Const Value: Cardinal);
Begin
    FReadTimeout := Value;
End;

Procedure TmnCustomCommStream.SetDiscardNull(Const Value: Boolean);
Begin
    FDiscardNull := Value;
End;

Procedure TmnCustomCommStream.SetEventChar(Const Value: AnsiChar);
Begin
    FEventChar := Value;
End;

Procedure TmnCustomCommStream.SetTimeout(Const Value: Cardinal);
Begin
    FTimeout := Value;
End;

Procedure TmnCustomCommStream.SetUseOverlapped(Const Value: Boolean);
Begin
    If FUseOverlapped <> Value Then
    Begin
        If Connected Then Raise EComPort.Create('Already connected');
        FUseOverlapped := Value;
    End;
End;

Procedure TmnCustomCommStream.SetWriteTimeout(Const Value: Cardinal);
Begin
    FWriteTimeout := Value;
End;

Function TmnCustomCommStream.WaitEvent(Const Events: TComEvents): TComEvents;
Begin
    Result := [];
End;

Function TmnCustomCommStream.Write(Const Buffer; Count: Integer): Integer;
Begin
    Result := DoWrite(Buffer, Count);
End;

End.
